package cn.mazexal.examapi.services;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.IdUtil;
import cn.mazexal.examapi.common.models.BizException;
import cn.mazexal.examapi.common.models.CustomerError;
import cn.mazexal.examapi.common.models.PagedData;
import cn.mazexal.examapi.dataware.FinNotificationLogMapper;
import cn.mazexal.examapi.dataware.FinOrderMapper;
import cn.mazexal.examapi.dataware.FinOrderPayMapper;
import cn.mazexal.examapi.dataware.FinPayAppleNotifyHisMapper;
import cn.mazexal.examapi.models.bo.PayNotify;
import cn.mazexal.examapi.models.converter.OrderConverter;
import cn.mazexal.examapi.models.db.*;
import cn.mazexal.examapi.models.enums.*;
import cn.mazexal.examapi.models.form.AppleyPayVerifyForm;
import cn.mazexal.examapi.models.qry.OrderQry;
import cn.mazexal.examapi.models.thirdpart.alipay.AliPayConfig;
import cn.mazexal.examapi.models.thirdpart.alipay.AlipayAppArgs;
import cn.mazexal.examapi.models.thirdpart.wxpay.WxPayConfigBO;
import cn.mazexal.examapi.models.thirdpart.wxpay.WxpayAppArgs;
import cn.mazexal.examapi.models.vo.AddVipOrderRes;
import cn.mazexal.examapi.models.vo.FinOrderVO;
import cn.mazexal.examapi.services.provider.AppleProvider;
import cn.mazexal.examapi.services.provider.PayProvider;
import cn.mazexal.examapi.utils.SpringContextUtil;
import com.alibaba.fastjson.JSON;
import com.apple.itunes.storekit.model.JWSRenewalInfoDecodedPayload;
import com.apple.itunes.storekit.model.JWSTransactionDecodedPayload;
import com.apple.itunes.storekit.model.Type;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author: Mazexal
 * Date: 2025/6/28
 */
@Service
@Slf4j
public class OrderService {

    @Resource
    private PayProvider payProvider;

    @Resource
    private CommonService commonService;
    @Resource
    private FinOrderPayMapper finOrderPayMapper;
    @Resource
    private FinOrderMapper finOrderMapper;

    @Resource
    private FinPayAppleNotifyHisMapper finPayAppleNotifyHisMapper;

    @Resource
    private UserService userService;

    @Resource
    private UserVipService userVipService;

    @Resource
    private AppleProvider appleProvider;

    @Resource
    private FinNotificationLogMapper finNotificationLogMapper;

    @Resource
    private OrderConverter orderConverter;

    @Transactional(rollbackFor = Exception.class)
    public String addOrderPayAndPayParam(FinOrderDO payOrderDO) throws Exception {
        Long orderId = payOrderDO.getId();
        PayTypeEnum payType = PayTypeEnum.getPayTypeEnum(payOrderDO.getPayType());
        long payCreateTime = System.currentTimeMillis();
        FinOrderPayDO payDO = new FinOrderPayDO();
        Long payId = IdUtil.getSnowflakeNextId();
        payDO.setId(payId);
        payDO.setOrderId(orderId);
        payDO.setPayType(payType.getValue());
        payDO.setPayStatus(PayStatusEnum.WAITTING.getValue());
        payDO.setPayCreateTime(payCreateTime);
        payDO.setPayWay(payOrderDO.getPayWay());
        payDO.setPayAmount(payOrderDO.getOrderAmount());
        payDO.setPaySubject(payOrderDO.getPaySubject());
        payDO.setPayBody(payOrderDO.getPayBody());

        String payParam = getPayParam(payOrderDO.getPayWay(), payOrderDO.getOrderAmount(), payOrderDO.getPaySubject()
                , payOrderDO.getPayBody(), payId, payOrderDO.getChannelId(), payOrderDO.getCreateIp());

        log.info("生成支付参数 {}", payParam);
        doAddOrderPayAndPayParam(payDO, payParam, payOrderDO);

        return payParam;
    }

    @Transactional(rollbackFor = Exception.class)
    public void doAddOrderPayAndPayParam(FinOrderPayDO payDO, String payParam, FinOrderDO payOrderDO) {
        payOrderDO.setPayParams(payParam);
        finOrderPayMapper.insert(payDO);
        finOrderMapper.updateById(payOrderDO);
    }


    public String getPayParam(Integer payWay, BigDecimal amount, String paySubject, String payBody, Long payId,
                              Long channelId, String ip) throws Exception {

        BigDecimal payAmount = SpringContextUtil.isDev() ? new BigDecimal("0.01") : amount;
        PayWayEnum payWayEnum = PayWayEnum.toEnum(payWay);
        String payParams;

        SysChannelDO channelDO = commonService.getChannelById(channelId);
        if (channelDO == null) {
            throw new BizException(new CustomerError("500", "未找到渠道信息"));
        }
        switch (payWayEnum) {
            case ALI_APP:
                Long aliPayConfigId = channelDO.getAliPayConfigId();
                String aliConfigStr = commonService.getPayConfigById(aliPayConfigId);
                AliPayConfig config = JSON.parseObject(aliConfigStr, AliPayConfig.class);
                AlipayAppArgs alipayAppArgs = AlipayAppArgs.builder()
                        .appId(config.getAppId())
                        .privateKey(config.getPrivateKey())
                        .alipayPublicKey(config.getAlipayPublicKey())
                        .signType(config.getSignType())
                        .totalAmount(payAmount.setScale(2, RoundingMode.HALF_UP).toPlainString())
                        .subject(paySubject)
                        .body(payBody)
                        .outTradeNo(String.valueOf(payId))
                        .configId(channelDO.getAliPayConfigId())
                        .build();
                payParams = payProvider.getAlipayAppParams(alipayAppArgs);
                break;

            case WX_APP:
                Long wxPayConfigId = channelDO.getAliPayConfigId();
                String wxConfigStr = commonService.getPayConfigById(wxPayConfigId);
                WxPayConfigBO wxPayConfigBO = JSON.parseObject(wxConfigStr, WxPayConfigBO.class);
                WxpayAppArgs wxpayAppArgs = WxpayAppArgs.builder()
                        .appId(wxPayConfigBO.getOpenAppId())
                        .mchId(wxPayConfigBO.getOpenMchId())
                        .apiKey(wxPayConfigBO.getOpenApiKey())
                        .body(paySubject)
                        .detail(payBody)
                        .outTradeNo(String.valueOf(payId))
                        .totalFee(payAmount.setScale(2, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).intValue())
                        .ip(ip)
                        .configId(wxPayConfigId)
                        .build();
                payParams = payProvider.getWxpayAppParams(wxpayAppArgs);
                break;

            default:
                throw new BizException(new CustomerError("500", "不支持的支付方式"));
        }
        return payParams;
    }

    public AddVipOrderRes addVipOrder(Long userId, Long channelId, String configIdStr, Integer payWay, String ip) {
        Long orderId = IdUtil.getSnowflakeNextId();
        VipPriceDO vipPriceDO;
        // 这里需要转换一下, 因为苹果支付的产品ID和数据库的ID不是同一个, 所以需要转换.
        if (channelId == 1) {
            vipPriceDO = commonService.getVipPriceDOByOutId(configIdStr, VipPriceOutTypeEnum.iOS);
        } else {
            vipPriceDO = commonService.getVipPriceDOById(Long.valueOf(configIdStr));
        }
        if (vipPriceDO == null) {
            throw new BizException(new CustomerError("500", "未找到商品价格信息"));
        }
        if (vipPriceDO.getPriceStatus() == 0) {
            throw new BizException(new CustomerError("500", "该商品已下架"));
        }

        int payCount = finOrderPayMapper.userSuccessPayCount(userId);
        // 苹果要求适用 RFC 4122, 禁止适用hutool
//        String uuid = IdUtil.simpleUUID();
        // 转换为字符串（自动符合8-4-4-4-12格式）
        UUID uuid = UUID.randomUUID();
        String uuidStr = uuid.toString();

        Long now = System.currentTimeMillis();
        FinOrderDO finOrderDO = new FinOrderDO();
        finOrderDO.setId(orderId);
        finOrderDO.setUuid(uuidStr);
        finOrderDO.setUserId(userId);
        finOrderDO.setPayType(PayTypeEnum.VIP.getValue());
        finOrderDO.setPayWay(payWay);
        finOrderDO.setOrderAmount(vipPriceDO.getPrice());
        finOrderDO.setPayStatus(PayStatusEnum.WAITTING.getValue());
        finOrderDO.setPayOrderCreateTime(now);
        finOrderDO.setChannelId(channelId);
        finOrderDO.setCreateIp(ip);
        finOrderDO.setPaySubject(getSubjectByVipPriceConfig(vipPriceDO));
        finOrderDO.setPayBody(vipPriceDO.getName());
        finOrderDO.setFirstRecharge(payCount == 0 ? 1 : 0);
        finOrderDO.setExtraInfo(JSON.toJSONString(vipPriceDO));
        finOrderMapper.insert(finOrderDO);
        log.info("创建订单成功 {}", finOrderDO);
        AddVipOrderRes res = new AddVipOrderRes();
        res.setOrderId(orderId);
        res.setUUID(uuidStr);
        return res;
    }

    private String getSubjectByVipPriceConfig(VipPriceDO vipPriceDO) {
        UnitEnum unitenum = UnitEnum.toEnum(vipPriceDO.getUnit());
        return "购买会员 " + vipPriceDO.getDuration() + unitenum.getName();
    }

    public FinOrderDO checkOrder(Long orderId) {
        FinOrderDO finOrderDO = finOrderMapper.selectById(orderId);
        if (finOrderDO == null) {
            throw new BizException(new CustomerError("500", "订单不存在"));
        }
        if (finOrderDO.getPayStatus() != PayStatusEnum.WAITTING.getValue()) {
            throw new BizException(new CustomerError("500", "订单支付状态异常, 请联系客服处理"));
        }
        return finOrderDO;
    }

    public void checkOrderPay(Long orderId) {
        FinOrderPayDO finOrderPayDO = finOrderPayMapper.selectByOrderId(orderId);
        if (finOrderPayDO != null) {
            throw new BizException(new CustomerError("500", "订单支付记录不存在"));
        }
    }


    public PayStatusEnum getPayStatus(Long orderId) {
        FinOrderDO finOrderDO = finOrderMapper.selectById(orderId);
        if (finOrderDO == null) {
            throw new BizException(new CustomerError("500", "订单不存在"));
        }
        return PayStatusEnum.toEnum(finOrderDO.getPayStatus());
    }

    public void addFinPayAppleNotifyHis(FinPayAppleNotifyHisDO payAppleNotifyHisDo) {
        finPayAppleNotifyHisMapper.insert(payAppleNotifyHisDo);
    }

    public void addFinPayAppleNotifyHisList(List<FinPayAppleNotifyHisDO> items) {
        finPayAppleNotifyHisMapper.insert(items);
    }

    @Transactional(rollbackFor = Exception.class)
    public boolean handlePayNotify(PayNotify notify) {
        Long payId = notify.getPayId();
        FinOrderPayDO payDO = finOrderPayMapper.selectByOrderId(payId);
        long curTime = System.currentTimeMillis();
        if (payDO == null || PayStatusEnum.WAITTING.getValue() != payDO.getPayStatus()) {
            log.info("订单已处理 payId={}", payId);
            finNotificationLogMapper.updateLogRes(notify.getNotifyLogId(), "SUCCESS", curTime);
            return true;
        }

        Long orderId = payDO.getOrderId();

        // 判断支付金额
//        BigDecimal payAmount = SpringContextUtil.isDev() ? new BigDecimal("0.01") : payDO.getPayAmount();
//        BigDecimal payAmount = payDO.getPayAmount();
//        int compareAmount = notify.getTotalAmount().setScale(2, RoundingMode.HALF_UP).compareTo(payAmount.setScale(2,
//                RoundingMode.HALF_UP));
//        if (compareAmount != 0) {
//            log.error("订单金额错误 orderId={} {} {} compare:{}", orderId, payAmount.doubleValue(),
//                    notify.getTotalAmount().doubleValue());
//            return false;
//        }

        log.info("修改支付单状态 payId={} 开始", payId);
        payDO.setPayStatus(PayStatusEnum.SUCCESSED.getValue());
        payDO.setPayNotifyTime(curTime);
        payDO.setPayBuyerId(notify.getBuyerId());
        payDO.setPayBuyerAccount(notify.getBuyerAccount());
        payDO.setPaySellerId(notify.getSellerId());
        payDO.setPaySellerAccount(notify.getSellerAccount());
        payDO.setTradeId(notify.getNotifyId());
        int update = finOrderPayMapper.updateById(payDO);
        log.info("修改支付单状态 payId={} 结束 update={}", payId, update);

        FinOrderDO finOrderDO = finOrderMapper.selectById(payDO.getOrderId());
        finOrderDO.setPayStatus(PayStatusEnum.SUCCESSED.getValue());
        finOrderDO.setPayId(payDO.getId());
        finOrderMapper.updateById(finOrderDO);
        // 业务处理
        if (finOrderDO.getPayType() == PayTypeEnum.VIP.getValue()) {
            VipPriceDO vipPriceDO = JSON.parseObject(finOrderDO.getExtraInfo(), VipPriceDO.class);
            userVipService.addUserVIP(finOrderDO.getUserId(), FromTypeEnum.BUY, orderId, payDO.getPayAmount(),
                    vipPriceDO);
            log.info("用户购买会员成功 userId={} orderId={}", finOrderDO.getUserId(), orderId);
        }
        finNotificationLogMapper.updateLogRes(notify.getNotifyLogId(), "SUCCESS", curTime);
        return true;
    }


    public boolean handleWxPayNotify(String xmlData) {
        try {
            WxPayOrderNotifyResult result = WxPayOrderNotifyResult.fromXML(xmlData);
            PayNotify payNotify = new PayNotify();
            payNotify.setPayId(Convert.toLong(result.getOutTradeNo()));
            payNotify.setBuyerId(result.getOpenid());
            payNotify.setBuyerAccount(result.getOpenid());
            payNotify.setSellerId(result.getMchId());
            payNotify.setSellerAccount(result.getMchId());
            payNotify.setTotalAmount(new BigDecimal(result.getTotalFee()).divide(new BigDecimal(100)));
            payNotify.setNotifyId(result.getTransactionId());
            return handlePayNotify(payNotify);
        } catch (Exception e) {
            log.error("微信异步通知处理失败", e);
        }
        return false;
    }

    @Transactional(rollbackFor = Exception.class)
    public boolean applePayVerify(Long userId, AppleyPayVerifyForm payParams) throws Exception {
        Long orderId = payParams.getOrderId();
        log.info("开始处理苹果支付校验 applePayVerify :{}", payParams);
        FinOrderDO finOrderDO = checkOrder(orderId);
//        FinOrderDO finOrderDO = finOrderMapper.selectById(orderId);
        if (!finOrderDO.getUserId().equals(userId)) {
            throw new BizException(new CustomerError("500", "订单不属于当前用户"));
        }
        // 添加绑定信息, 等苹果回调确定.
        String signedTransactionInfo = payParams.getSignedTransactionInfo();
        JWSTransactionDecodedPayload payload =
                appleProvider.verifyTransaction(signedTransactionInfo);
        String transactionId = payload.getTransactionId();
        if (payload.getTransactionId() == null || payload.getPurchaseDate() == null) {
            log.error("苹果支付信息错误 orderId={} transactionId={}, payload ={}", orderId, transactionId, payload);
            return false;
        }
        String productId = payload.getProductId();
        VipPriceDO vipPriceDO = commonService.getVipPriceDOByOutId(productId, VipPriceOutTypeEnum.iOS);

        Type type = payload.getType();
        if (type == Type.NON_RENEWING_SUBSCRIPTION) {
            // 因为一次性购买不用考虑升降级, 直接处理
            handlePay(payload, orderId, transactionId, finOrderDO, payParams, vipPriceDO, true);
        }
        if(type == Type.AUTO_RENEWABLE_SUBSCRIPTION){
            // 这里通过是否存在VIP来判断, 如果存在, 那么说明是续费, 通过回调处理, 如果不是VIP, 需要立马处理, 这样就可以省去判断是否升降级的问题.
            boolean isVip = userVipService.isVip(userId, vipPriceDO.getExamBankId());
            if (isVip) {
                // 通过回调处理
                handlePay(payload, orderId, transactionId, finOrderDO, payParams, vipPriceDO, false);
            } else {
                // 立马处理.不需要考虑升降级
                handlePay(payload, orderId, transactionId, finOrderDO, payParams, vipPriceDO, true);
            }
        }
        return true;
    }


    @Transactional(rollbackFor = Exception.class)
    public void handlePay(JWSTransactionDecodedPayload payload, Long orderId, String transactionId,
                          FinOrderDO finOrderDO, AppleyPayVerifyForm payParams, VipPriceDO vipPriceDO,
                          boolean needHandleNow) {
        Long payId = IdUtil.getSnowflakeNextId();
        String payParam = JSON.toJSONString(payload);
        log.info("苹果支付信息 orderId={} transactionId={}, payload ={}", orderId, transactionId, payload);
        finOrderDO.setAppleTransactionId(transactionId);
        finOrderDO.setAppleReceiptData(payParams.getSignedTransactionInfo());
        // 因为苹果的支付和订单是一一对应的, 所以可以直接设置.
        finOrderDO.setPayId(payId);
        finOrderDO.setPayParams(payParam);
        // 修改订单状态, 因为是苹果的支付, 校验通过就算是成功.
        if (needHandleNow) {
            finOrderDO.setPayStatus(PayStatusEnum.SUCCESSED.getValue());
        } else {
            finOrderDO.setPayStatus(PayStatusEnum.WAITTING.getValue());
        }
        finOrderDO.setOrderAmount(new BigDecimal(payload.getPrice()).multiply(new BigDecimal(payload.getQuantity())).divide(new BigDecimal(100)));
        finOrderMapper.updateById(finOrderDO);
        // 这里需要插入orderPay表数据. 因为苹果的没有获取支付参数, 而是之前前端支付以后再来校验的.
        PayTypeEnum payType = PayTypeEnum.getPayTypeEnum(finOrderDO.getPayType());
        Long now = System.currentTimeMillis();
        FinOrderPayDO payDO = new FinOrderPayDO();
        payDO.setId(payId);
        payDO.setOrderId(orderId);
        payDO.setPayType(payType.getValue());
        if (needHandleNow) {
            payDO.setPayStatus(PayStatusEnum.SUCCESSED.getValue());
        } else {
            payDO.setPayStatus(PayStatusEnum.WAITTING.getValue());
        }
        payDO.setPayCreateTime(now);
        payDO.setPayWay(PayWayEnum.APPLE.getValue());
        payDO.setPayAmount(finOrderDO.getOrderAmount());
        payDO.setPaySubject(finOrderDO.getPaySubject());
        payDO.setPayBody(finOrderDO.getPayBody());
        finOrderPayMapper.insert(payDO);
        if (needHandleNow) {
            userVipService.addUserVIP(finOrderDO.getUserId(), FromTypeEnum.BUY, orderId, payDO.getPayAmount(),
                    vipPriceDO);
        }
    }

    public void addFinNotification(FinNotificationLogDO finNotificationLogDO) {
        finNotificationLogMapper.insert(finNotificationLogDO);
    }

    public FinOrderDO getAppleOrderByTransactionId(String transactionId) {
        return finOrderMapper.selectAppleOrderByTransactionId(transactionId);

    }

    @Transactional(rollbackFor = Exception.class)
    public void handleRefund(FinOrderDO finOrderDO, JWSTransactionDecodedPayload transaction, Long logId) {
        Long now = System.currentTimeMillis();
        finOrderDO.setPayStatus(PayStatusEnum.REFUND.getValue());
        FinOrderPayDO payDO = finOrderPayMapper.selectById(finOrderDO.getPayId());
        String transactionId = transaction.getTransactionId();
        finOrderMapper.updateById(finOrderDO);
        // 这里给退款的订单重新生成一个支付信息
        payDO.setId(IdUtil.getSnowflakeNextId());
        payDO.setPayStatus(PayStatusEnum.REFUND.getValue());
        payDO.setTradeId(transactionId);
        payDO.setPayBody(JSON.toJSONString(transaction));
        payDO.setPayCreateTime(now);
        payDO.setPayNotifyTime(now);
        finOrderPayMapper.insert(payDO);
        userVipService.handleRefund(finOrderDO, FromTypeEnum.REFUND);
        finNotificationLogMapper.updateLogRes(logId, "SUCCESS", now);
    }

    @Transactional(rollbackFor = Exception.class)
    public void addResumeOrder(JWSRenewalInfoDecodedPayload renewalInfo,
                               JWSTransactionDecodedPayload transaction,
                               Long notifyLogId) {
        Long orderId = IdUtil.getSnowflakeNextId();
        Long now = System.currentTimeMillis();
        // 这里没想到是用原来的直接套用, 还是用新的
//        VipPriceDO vipPriceDO;
//        // 这里需要转换一下, 因为苹果支付的产品ID和数据库的ID不是同一个, 所以需要转换.
//        Long configId = Long.valueOf(transaction.getProductId());
//        vipPriceDO = commonService.getVipPriceDOByOutId(configId, VipPriceOutTypeEnum.iOS);
        String originalTransactionId = transaction.getOriginalTransactionId();

        String transactionId = transaction.getTransactionId();
        // 这就是最初的订单
        FinOrderDO originalFinOrder = finOrderMapper.selectAppleOrderByTransactionId(originalTransactionId);
        FinOrderPayDO originalPayDO = finOrderPayMapper.selectById(originalFinOrder.getPayId());


        Long payId = IdUtil.getSnowflakeNextId();
        String transactionStr = JSON.toJSONString(transaction);

        originalFinOrder.setId(orderId);
        originalFinOrder.setFirstRecharge(0);
        originalFinOrder.setPayId(payId);
        originalFinOrder.setAppleReceiptData(transactionStr);
        originalFinOrder.setPayOrderCreateTime(now);
        originalFinOrder.setOrderAmount(new BigDecimal(transaction.getPrice()).divide(BigDecimal.valueOf(100)));

        originalPayDO.setId(payId);
        originalPayDO.setOrderId(orderId);
        originalPayDO.setTradeId(transactionId);
        originalPayDO.setPayCreateTime(now);
        originalPayDO.setPayNotifyTime(now);
        originalPayDO.setPayBody(transactionStr);


        finOrderMapper.insert(originalFinOrder);
        finOrderPayMapper.insert(originalPayDO);
        finNotificationLogMapper.updateLogRes(notifyLogId, "SUCCESS", now);

        // 因为考虑到升降级, 所以需要处理
        VipPriceDO vipPriceDO = commonService.getVipPriceDOByOutId(transaction.getProductId(), VipPriceOutTypeEnum.iOS);

        userVipService.addUserVIP(originalFinOrder.getUserId(), FromTypeEnum.RESUME, orderId,
                originalPayDO.getPayAmount(), vipPriceDO);
    }


    public PagedData<FinOrderVO> getOrderList(OrderQry qry) {
        List<FinOrderDO> finOrderDOList = finOrderMapper.getOrderList(qry);
        int count = finOrderMapper.getOrderListCount(qry);
        return PagedData.of(qry, count,
                finOrderDOList.stream().map(orderConverter::toOrderView).collect(Collectors.toList()));
    }

    public FinOrderDO getAppleOrderByUUID(String uuid) {
        return finOrderMapper.selectAppleOrderByUUID(uuid);
    }

    public FinNotificationLogDO getFinPayAppleNotifyHis(String notificationUUID) {
        return finNotificationLogMapper.selectByUUID(notificationUUID);
    }
}
